<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Keep-Alive 组件生命周期</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    body {
      background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
      color: #333;
      line-height: 1.6;
      padding: 20px;
      min-height: 100vh;
    }

    .container {
      max-width: 1200px;
      margin: 0 auto;
      background: white;
      border-radius: 15px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
      overflow: hidden;
    }

    header {
      background: #4a6bdf;
      color: white;
      padding: 25px;
      text-align: center;
    }

    h1 {
      font-size: 2.2rem;
      margin-bottom: 10px;
    }

    .subtitle {
      font-size: 1.1rem;
      opacity: 0.9;
    }

    .content {
      display: flex;
      flex-wrap: wrap;
      padding: 20px;
    }

    .explanation {
      flex: 1;
      min-width: 300px;
      padding: 20px;
    }

    .demo {
      flex: 1;
      min-width: 300px;
      padding: 20px;
      display: flex;
      flex-direction: column;
    }

    .card {
      background: white;
      border-radius: 10px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
      padding: 20px;
      margin-bottom: 20px;
    }

    h2 {
      color: #4a6bdf;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 2px solid #eaeaea;
    }

    h3 {
      color: #4a6bdf;
      margin: 15px 0 10px;
    }

    p {
      margin-bottom: 15px;
    }

    ul,
    ol {
      margin-left: 20px;
      margin-bottom: 15px;
    }

    li {
      margin-bottom: 8px;
      padding-left: 5px;
    }

    .lifecycle {
      display: flex;
      justify-content: space-between;
      margin: 20px 0;
      flex-wrap: wrap;
    }

    .lifecycle-phase {
      flex: 1;
      min-width: 250px;
      background: #f8f9fa;
      border-radius: 8px;
      padding: 15px;
      margin: 10px;
    }

    .lifecycle-phase h3 {
      text-align: center;
      color: #4a6bdf;
    }

    .lifecycle-item {
      padding: 10px;
      margin: 8px 0;
      background: white;
      border-radius: 5px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
    }

    .first-time {
      border-left: 4px solid #38a169;
    }

    .second-time {
      border-left: 4px solid #d69e2e;
    }

    .controls {
      display: flex;
      gap: 10px;
      margin: 15px 0;
      flex-wrap: wrap;
    }

    button {
      padding: 10px 15px;
      border: none;
      border-radius: 5px;
      background: #4a6bdf;
      color: white;
      cursor: pointer;
      transition: background 0.3s;
    }

    button:hover {
      background: #3a5bce;
    }

    button:disabled {
      background: #a0aec0;
      cursor: not-allowed;
    }

    button.active {
      background: #38a169;
    }

    .component-container {
      border: 2px solid #e2e8f0;
      border-radius: 8px;
      padding: 20px;
      margin: 15px 0;
      min-height: 200px;
      background: #f8f9fa;
    }

    .component {
      padding: 20px;
      background: white;
      border-radius: 8px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
    }

    .component h3 {
      text-align: center;
      color: #4a6bdf;
    }

    .log-container {
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 15px;
      margin: 15px 0;
      overflow-y: auto;
      max-height: 300px;
      background: #2d3748;
      color: #e2e8f0;
      font-family: 'Fira Code', monospace;
    }

    .log-entry {
      margin-bottom: 8px;
      padding: 5px;
      border-radius: 3px;
    }

    .log-entry.created {
      background: rgba(72, 187, 120, 0.1);
    }

    .log-entry.mounted {
      background: rgba(66, 153, 225, 0.1);
    }

    .log-entry.activated {
      background: rgba(246, 173, 85, 0.1);
    }

    .log-entry.deactivated {
      background: rgba(237, 100, 166, 0.1);
    }

    .log-entry.destroyed {
      background: rgba(245, 101, 101, 0.1);
    }

    .highlight {
      animation: highlight 1s ease;
    }

    @keyframes highlight {
      0% {
        background-color: rgba(255, 255, 0, 0.3);
      }

      100% {
        background-color: transparent;
      }
    }

    @media (max-width: 768px) {
      .content {
        flex-direction: column;
      }

      .lifecycle {
        flex-direction: column;
      }
    }
  </style>
</head>

<body>
  <div class="container">
    <header>
      <h1>Keep-Alive 组件生命周期</h1>
      <p class="subtitle">详解被 keep-alive 包裹的组件生命周期执行顺序</p>
    </header>

    <div class="content">
      <div class="explanation">
        <div class="card">
          <h2>Keep-Alive 组件生命周期</h2>
          <p>当组件被 <code>&lt;keep-alive&gt;</code> 包裹时，它的生命周期行为会与普通组件有所不同。</p>

          <h3>首次进入组件</h3>
          <ol>
            <li><strong>beforeCreate</strong> - 组件实例刚被创建</li>
            <li><strong>created</strong> - 组件实例创建完成</li>
            <li><strong>beforeMount</strong> - 组件挂载之前</li>
            <li><strong>mounted</strong> - 组件挂载到DOM</li>
            <li><strong>activated</strong> - 组件被激活（keep-alive特有）</li>
          </ol>

          <h3>再次进入组件</h3>
          <ol>
            <li><strong>activated</strong> - 组件被激活（keep-alive特有）</li>
          </ol>

          <h3>离开组件</h3>
          <ol>
            <li><strong>deactivated</strong> - 组件被停用（keep-alive特有）</li>
          </ol>
        </div>

        <div class="card">
          <h2>生命周期流程图</h2>
          <div class="lifecycle">
            <div class="lifecycle-phase">
              <h3>首次进入</h3>
              <div class="lifecycle-item first-time">beforeCreate</div>
              <div class="lifecycle-item first-time">created</div>
              <div class="lifecycle-item first-time">beforeMount</div>
              <div class="lifecycle-item first-time">mounted</div>
              <div class="lifecycle-item first-time">activated</div>
            </div>

            <div class="lifecycle-phase">
              <h3>再次进入</h3>
              <div class="lifecycle-item second-time">activated</div>
            </div>

            <div class="lifecycle-phase">
              <h3>离开组件</h3>
              <div class="lifecycle-item">deactivated</div>
            </div>
          </div>
        </div>

        <div class="card">
          <h2>注意事项</h2>
          <ul>
            <li>被 keep-alive 包裹的组件不会调用 <strong>beforeDestroy</strong> 和 <strong>destroyed</strong> 钩子</li>
            <li>组件切换时，会触发 <strong>activated</strong> 和 <strong>deactivated</strong> 钩子</li>
            <li>组件状态会被缓存，包括数据、DOM状态等</li>
            <li>适合缓存需要保持状态的组件，如标签页、表单等</li>
          </ul>
        </div>
      </div>

      <div class="demo">
        <div class="card">
          <h2>演示</h2>
          <p>切换下面的按钮来模拟进入和离开 keep-alive 组件：</p>

          <div class="controls">
            <button id="firstEnter" class="active">首次进入组件</button>
            <button id="leave" disabled>离开组件</button>
            <button id="secondEnter" disabled>再次进入组件</button>
            <button id="reset">重置演示</button>
          </div>

          <div class="component-container">
            <div class="component">
              <h3>Keep-Alive 组件</h3>
              <p id="componentState">当前状态: 未初始化</p>
              <p>计数器: <span id="counter">0</span></p>
              <button id="increment">增加计数</button>
            </div>
          </div>

          <h3>生命周期日志</h3>
          <div class="log-container" id="logContainer">
            <div class="log-entry">等待组件生命周期触发...</div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    // 模拟组件状态
    let componentInstance = {
      data: {
        count: 0,
        initialized: false
      },
      created() {
        addLog('created', '组件实例创建完成');
      },
      mounted() {
        addLog('mounted', '组件挂载到DOM');
        updateComponentState('已挂载');
      },
      activated() {
        addLog('activated', '组件被激活');
        updateComponentState('已激活');
      },
      deactivated() {
        addLog('deactivated', '组件被停用');
        updateComponentState('已停用（缓存中）');
      }
    };

    // DOM元素
    const firstEnterBtn = document.getElementById('firstEnter');
    const leaveBtn = document.getElementById('leave');
    const secondEnterBtn = document.getElementById('secondEnter');
    const resetBtn = document.getElementById('reset');
    const logContainer = document.getElementById('logContainer');
    const componentState = document.getElementById('componentState');
    const counterElement = document.getElementById('counter');
    const incrementBtn = document.getElementById('increment');

    // 初始化
    let currentState = 'none'; // none, active, inactive
    let isFirstTime = true;

    // 添加日志
    function addLog(hook, message) {
      const logEntry = document.createElement('div');
      logEntry.className = `log-entry ${hook}`;

      const timestamp = new Date().toLocaleTimeString();
      logEntry.textContent = `[${timestamp}] ${hook}: ${message}`;

      logContainer.appendChild(logEntry);
      logEntry.scrollIntoView({ behavior: 'smooth' });

      // 添加高亮效果
      logEntry.classList.add('highlight');
      setTimeout(() => {
        logEntry.classList.remove('highlight');
      }, 1000);
    }

    // 更新组件状态显示
    function updateComponentState(state) {
      componentState.textContent = `当前状态: ${state}`;
    }

    // 更新计数器显示
    function updateCounter() {
      counterElement.textContent = componentInstance.data.count;
    }

    // 首次进入组件
    function firstEnterComponent() {
      if (currentState !== 'none') return;

      addLog('beforeCreate', '组件实例刚被创建');
      componentInstance.created();
      addLog('beforeMount', '组件挂载之前');
      componentInstance.mounted();
      componentInstance.activated();

      currentState = 'active';
      isFirstTime = false;

      firstEnterBtn.disabled = true;
      leaveBtn.disabled = false;
      secondEnterBtn.disabled = true;

      // 更新按钮状态
      firstEnterBtn.classList.remove('active');
      leaveBtn.classList.add('active');
    }

    // 离开组件
    function leaveComponent() {
      if (currentState !== 'active') return;

      componentInstance.deactivated();

      currentState = 'inactive';

      leaveBtn.disabled = true;
      secondEnterBtn.disabled = false;

      // 更新按钮状态
      leaveBtn.classList.remove('active');
      secondEnterBtn.classList.add('active');
    }

    // 再次进入组件
    function secondEnterComponent() {
      if (currentState !== 'inactive') return;

      componentInstance.activated();

      currentState = 'active';

      secondEnterBtn.disabled = true;
      leaveBtn.disabled = false;

      // 更新按钮状态
      secondEnterBtn.classList.remove('active');
      leaveBtn.classList.add('active');
    }

    // 重置演示
    function resetDemo() {
      logContainer.innerHTML = '<div class="log-entry">等待组件生命周期触发...</div>';
      updateComponentState('未初始化');
      componentInstance.data.count = 0;
      updateCounter();

      currentState = 'none';
      isFirstTime = true;

      firstEnterBtn.disabled = false;
      leaveBtn.disabled = true;
      secondEnterBtn.disabled = true;

      // 更新按钮状态
      firstEnterBtn.classList.add('active');
      leaveBtn.classList.remove('active');
      secondEnterBtn.classList.remove('active');
    }

    // 增加计数
    function incrementCounter() {
      if (currentState !== 'active') return;

      componentInstance.data.count++;
      updateCounter();
      addLog('data-change', `计数器更新为: ${componentInstance.data.count}`);
    }

    // 事件监听
    firstEnterBtn.addEventListener('click', firstEnterComponent);
    leaveBtn.addEventListener('click', leaveComponent);
    secondEnterBtn.addEventListener('click', secondEnterComponent);
    resetBtn.addEventListener('click', resetDemo);
    incrementBtn.addEventListener('click', incrementCounter);

    // 初始化UI状态
    updateCounter();
  </script>
</body>

</html>